package com.core.shared;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.io.File;
import java.io.FileInputStream;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Random;

/**
 * Created with IntelliJ IDEA.
 * User: lxf
 * Date: 2016/10/18
 * Time: 14:59
 * To change this template use Editor | File and Code Templates.
 */
public class QRCodeHandle {

    private static final int qrcodeWidth = 300;
    private static final int qrcodeHeight = 300;
    private static final int image_width = 68;
    private static final int image_height = 68;
    private static final int image_half_width = image_width / 2;
    private static final int frame_width = 5;
    private static final String qrcodeFormat = "png";
    private static final Logger logger = LoggerFactory.getLogger(QRCodeHandle.class);
    private static final HashMap<EncodeHintType, Object> encodeHints = new HashMap<>();
    private static final HashMap<DecodeHintType, Object> decodeHints = new HashMap<>();
    private static String[] image_type = {"bmp", "jpg", "jpeg", "png", "gif", "icon"};

    static {
        encodeHints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        decodeHints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
    }

    /**
     * 生成二维码
     *
     * @param content
     * @return 二维码物理地址
     */
    public static String encodeQrcode(String content) {
        Random random = new Random();
        String qrcodeFilePath = "f:/qrcode/" + random.nextInt() + "." + qrcodeFormat;
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, qrcodeWidth, qrcodeHeight, encodeHints);
            Path path = FileSystems.getDefault().getPath(qrcodeFilePath);
            MatrixToImageWriter.writeToPath(bitMatrix, qrcodeFormat, path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return qrcodeFilePath;
    }

    /**
     * 生成二维码输出流
     *
     * @param content
     * @param outputStream
     */
    public static void encodeQrcode(String content, OutputStream outputStream) {
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, qrcodeWidth, qrcodeHeight, encodeHints);
            MatrixToImageWriter.writeToStream(bitMatrix, qrcodeFormat, outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成带图标的二维码
     *
     * @param content       内容
     * @param iconImagePath 图标地址
     * @param hasFiller     是否需要补白
     * @return 二维码物理地址
     */
    public static String encodeQrcodeWithImage(String content, String iconImagePath, boolean hasFiller) {
        Random random = new Random();
        String qrcodeFilePath = "f:/qrcode/" + random.nextInt() + "." + qrcodeFormat;
        logger.debug("QRCodeHandle.encodeQrcodeWithImage the qrcodeFilePath is {}", qrcodeFilePath);
        try {
            File icon = new File(iconImagePath);
            if (!icon.exists()) {
                logger.error("QRCodeHandle.encodeQrcodeWithImage the iconImage is not exists,[iconImagePath:{}]", iconImagePath);
                return null;
            }
            String iconFileName = icon.getName();
            String fileExtName = iconFileName.substring(iconFileName.lastIndexOf(".") + 1);
            if (!checkIMGType(fileExtName)) {
                logger.error("QRCodeHandle.encodeQrcodeWithImage the iconImage type is not support,[iconImageType:{}]", fileExtName);
                return null;
            }
            ImageIO.write(genBarcode(content, qrcodeWidth, qrcodeHeight, iconImagePath, hasFiller), qrcodeFormat, new File(qrcodeFilePath));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return qrcodeFilePath;
    }

    /**
     * 解析二维码
     *
     * @param filePath 二维码地址
     * @return 二维码内容
     */
    public static String decodeQrcode(String filePath) {
        String retStr = "";
        if (CommonUtils.isEmpty(filePath)) {
            logger.error("QRCodeHandle.decodeQrcode,param filePath is null");
            return retStr;
        }
        try {
            BufferedImage bufferedImage = ImageIO.read(new FileInputStream(filePath));
            LuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
            Binarizer binarizer = new HybridBinarizer(source);
            BinaryBitmap bitmap = new BinaryBitmap(binarizer);
            Result result = new MultiFormatReader().decode(bitmap, decodeHints);
            retStr = result.getText();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retStr;
    }

    private static BufferedImage genBarcode(String content, int width, int height, String iconImagePath, boolean hasFiller) throws WriterException, IOException {
        // 读取源图像
        BufferedImage scaleImage = scale(iconImagePath, image_width, image_height, hasFiller);
        int[][] srcPixels = new int[image_width][image_height];
        for (int i = 0; i < scaleImage.getWidth(); i++) {
            for (int j = 0; j < scaleImage.getHeight(); j++) {
                srcPixels[i][j] = scaleImage.getRGB(i, j);
            }
        }

        encodeHints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 生成二维码
        BitMatrix matrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, encodeHints);

        // 二维矩阵转为一维像素数组
        int halfW = matrix.getWidth() / 2;
        int halfH = matrix.getHeight() / 2;
        int[] pixels = new int[width * height];

        for (int y = 0; y < matrix.getHeight(); y++) {
            for (int x = 0; x < matrix.getWidth(); x++) {
                // 读取图片
                if (x > halfW - image_half_width && x < halfW + image_half_width && y > halfH - image_half_width && y < halfH + image_half_width) {
                    pixels[y * width + x] = srcPixels[x - halfW + image_half_width][y - halfH + image_half_width];
                }
                // 在图片四周形成边框
                else if ((x > halfW - image_half_width - frame_width
                        && x < halfW - image_half_width + frame_width
                        && y > halfH - image_half_width - frame_width && y < halfH
                        + image_half_width + frame_width)
                        || (x > halfW + image_half_width - frame_width
                        && x < halfW + image_half_width + frame_width
                        && y > halfH - image_half_width - frame_width && y < halfH
                        + image_half_width + frame_width)
                        || (x > halfW - image_half_width - frame_width
                        && x < halfW + image_half_width + frame_width
                        && y > halfH - image_half_width - frame_width && y < halfH
                        - image_half_width + frame_width)
                        || (x > halfW - image_half_width - frame_width
                        && x < halfW + image_half_width + frame_width
                        && y > halfH + image_half_width - frame_width && y < halfH
                        + image_half_width + frame_width)) {
                    pixels[y * width + x] = ColorUtils.white;
                } else {
                    // 此处可以分别制定二维码和背景的颜色；
                    pixels[y * width + x] = matrix.get(x, y) ? ColorUtils.black : ColorUtils.white;
                }
            }
        }

        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        image.getRaster().setDataElements(0, 0, width, height, pixels);
        return image;
    }

    private static BufferedImage scale(String iconImagePath, int height, int width, boolean hasFiller) throws IOException {
        double ratio = 0.0; // 缩放比例
        File file = new File(iconImagePath);
        BufferedImage srcImage = ImageIO.read(file);
        Image destImage = srcImage.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
        // 计算比例
        if ((srcImage.getHeight() > height) || (srcImage.getWidth() > width)) {
            if (srcImage.getHeight() > srcImage.getWidth()) {
                ratio = (new Integer(height)).doubleValue() / srcImage.getHeight();
            } else {
                ratio = (new Integer(width)).doubleValue() / srcImage.getWidth();
            }
            AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
            destImage = op.filter(srcImage, null);
        }
        if (hasFiller) {// 补白
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D graphic = image.createGraphics();
            graphic.setColor(Color.white);
            graphic.fillRect(0, 0, width, height);
            if (width == destImage.getWidth(null)) {
                graphic.drawImage(destImage, 0, (height - destImage.getHeight(null)) / 2, destImage.getWidth(null), destImage.getHeight(null), Color.white, null);
            } else {
                graphic.drawImage(destImage, (width - destImage.getWidth(null)) / 2, 0, destImage.getWidth(null), destImage.getHeight(null), Color.white, null);
            }
            graphic.dispose();
            destImage = image;
        }
        return (BufferedImage) destImage;
    }

    private static boolean checkIMGType(String fileExtName) {
        for (String type : image_type) {
            if (type.equalsIgnoreCase(fileExtName.toLowerCase())) {
                return true;
            }
        }
        return false;
    }


}
